home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-08-20 | 18.8 KB | 666 lines | [TEXT/CCL ] |
-
- ~---------------------------------------------------------------------------------------~
- ~ Rules for Plisp (Pattern Lisp) ~
- ~---------------------------------------------------------------------------------------~
-
- -define language Plisp-
-
-
- -Lisp-
-
- `(export '(plispProgram braceExpression definePlispFunction) :glisp)
-
-
- -Plisp-
-
- plispProgram =
- ~ a Plisp program is a sequence of Plisp functions each terminated by a semicolon
-
- <sourceLanguage Plisp> [ <plispFunction>:fns '; <flush> ]* ->
- (<reservedWords> ::fns);
-
-
- plispFunction =
- ~ a Plisp function is a name, an = sign, and a sequence of zero or more rules
- ~ separated by commas. Optionally "appearance order" and "add/remove rule(s)"
- ~ may appear.
-
- <pFunction>:name '= <plispRules>:rules ->
- <definePlispFunction :name :rules nil>,
-
- <pFunction>:name '\( appearance order ') '= <plispRules>:rules ->
- <definePlispFunction :name :rules t>,
-
- <pFunction>:name '\( add [rule | rules] ') '= <plispRules>:rules ->
- (addRules (quote :name) (quote :rules) nil),
-
- <pFunction>:name '\( add [rule | rules] ', appearance order ') '=
- <plispRules>:rules ->
- (addRules (quote :name) (quote :rules) t),
-
- <pFunction>:name '\( remove [rule | rules] ') '= <plispRules>:rules ->
- (removeRules (quote :name) (quote :rules));
-
-
- plispRules =
- [<plispRule>:r / ',]* -> :r;
-
-
- plispRule =
- <lhs> <pattern>:p1 '-'> <rhs> <pattern>:p2 -> (::p1 (rewritesTo) ::p2);
-
-
- pattern =
- [<item>:i]* -> :i;
-
-
- item =
- <aLiteral>:lit -> ~ x
- (literal :lit),
-
- ': <pVariable>:var -> ~ :x
- (variable :var),
-
- ':': <pVariable>:var <pattern>:pat -> ~ ::x
- (variable :var t :pat),
-
- ':': <pVariable>:var ') -> ~ ::x)
- (variable :var t ((endList))),
-
- '.'. <pVariable '.>:var -> ~ ..
- (variable :var),
-
- '.'.'. <pVariable '.>:var <pattern>:pat -> ~ ...
- (variable :var t :pat),
-
- '.'.'. <pVariable '.>:var ') -> ~ ...)
- (variable :var t ((endList))),
-
- '< <fnName>:fn '> -> ~ <foo>
- (call :fn),
-
- '< <fnName>:fn <rhs> <pattern>:pat <phs> '> -> ~ <foo :x 10 "abc">
- (call :fn :pat),
-
- '<'< <fnName>:fn '>'> -> ~ <<foo>>
- (call :fn nil t),
-
- '<'< <fnName>:fn <rhs> <pattern>:pat <phs> '>'> -> ~ <<foo :x 10 "abc">>
- (call :fn :pat t),
-
- '[ <pVariable '[ >:v <pattern>:pat '] ~ [a]
- <controlVar :v>:cvar ->
- (alternatives :cvar :pat nil),
-
- '[ <pVariable '[ >:v <pattern>:pat '] ~ [a]*
- ['*|'+] <controlVar :v>:cvar ->
- (repeat :cvar [0|1] :pat),
-
- '[ <pVariable '[ >:v <pattern>:pat '/ ~ [a / ',]*
- <pattern>:sep '] ['*|'+] <controlVar :v>:cvar ->
- (repeat :cvar [0|1] :pat :sep),
-
- '[ <pVariable '[ >:v <pattern>:pat '| ~ [a | b | c]
- [<pattern>:p / '|]* '] <controlVar :v>:cvar ->
- (alternatives :cvar :pat ::p),
-
- '[ <pVariable '[ >:v <pattern>:pat '| ~ [a | b | c]*
- [<pattern>:p / '|]* '] ['*|'+]=:n <controlVar :v>:cvar ->
- (repeat :cvar [0|1]=:n ((alternatives <pVariable '|> :pat ::p))),
-
- '[ <pVariable '[ >:v <pattern>:pat '| ~ [a | b | c / ',]*
- [<pattern>:p / '|]* '/ <pattern>:sep ']
- ['*|'+]=:n <controlVar :v>:cvar ->
- (repeat :cvar [0|1]=:n ((alternatives <pVariable '|> :pat ::p)) :sep),
-
- '\( -> (beginList), ~ ( with no preceding '
-
- ') -> (endList), ~ ) with no preceding '
-
- '{ if <braceExpression>:e ~ {if (> foo 10)}
- [': failMessage <braceExpression>:msg] '} ->
- (lisp if :e [:msg]),
-
- '{ do <braceExpression>:e '} -> ~ {do (setq foo 10)}
- (lisp do :e),
-
- '{ value <braceExpression>:e ~ {value (append x y)}
- [': failMessage <braceExpression>:msg] '} ->
- (lisp value :e nil [:msg]),
-
- '{'{ value <braceExpression>:e ~ {{value (append x y)}}
- [': failMessage <braceExpression>:msg] '}'} ->
- (lisp value :e t [:msg]);
-
-
- aLiteral =
- <identifier>:id -> <makeReservedWord :id>, ~ a
-
- <aNumber> -> , ~ 10
-
- <aString> <onRight> -> , ~ "abc" (right side only)
-
- ~ we can't allow strings on the left side because (case ...) can't handle them,
- ~ and (literals ...) is translated to (case ...). we need to fix this someday.
-
- <aString> <onLeft> ->
- <pError "strings are not currently allowed on the left sides of rules">,
-
- '' -> ; ~ ' <any data type>, e.g. '\( ') 'foo
-
-
- ~---------------------------------------------------------------------------------------~
- ~ Miscellaneous rules ~
- ~---------------------------------------------------------------------------------------~
-
- definePlispFunction =
- ~ defines a Plisp function from scratch
- ~ :name (list of rules) t/nil--keep in appearance order? -> (defpfun ...)
- ~ the value is a call to the defpfun macro:
- ~ (defpfun name args expanded-rules rule-tree)
-
- :name :rules :appearanceOrder
- <mergeRules <reverse :rules> nil :appearanceOrder>
- <expandRules :name>:def -> :def;
-
-
- pFunction =
- ~ checks if a Plisp function is being redefined
-
- <identifier>:name <checkFunction :name> -> :name;
-
-
- reparsePlispFunction =
- ~ for use by 'reparse'
-
- <plispFunction>:fn '; -> :fn;
-
-
- fnName =
- ~ either identifiers or symbols preceded by a single quote (') may appear
- ~ inside angle brackets < >: <foo x y> or <'+ x y>
-
- [<identifier> | ''] -> ;
-
-
- controlVar =
- ~ looks for a control variable; e.g. [a]* = :n, [a|b|c] = :alt.
- ~ if controls are not given explicit names, they are matched up on the left and
- ~ right sides of a rule in the order in which their "[" appear
-
- :n ['= ': <pVariable>:var] -> [:var <restoreName '[ :n> | :n];
-
-
- braceExpression = ;
- ~ this controls what type of expression is allowed to be inside braces { }.
- ~ the default is ordinary Lisp s-expressions (read with the Glisp read table).
-
-
- removeRule =
- ~ removes a rule from a rule tree.
- ~ this needs to return a value, because it's called from a Lisp function.
- ~ :rule :tree -> :newtree
-
- :rule :tree <linearMatch :rule :tree> 't -> nil,
-
- :rule :tree <removeRule2 :rule :tree>:newtree -> :newtree,
-
- :rule :tree ->
- <pWarning "couldn't remove the rule
- " <ruleToString :rule> "
- from
- " <ruleToString :tree>>
- :tree;
-
-
- removeRule2 =
- ~ subroutine; fails if it can't remove the rule from the tree
- ~ :rule :tree -> [:newtree | <failure>]
-
- ((rewritesTo) ...) ((rewritesTo) ...) -> nil,
-
- (:x ...) (:x ...) -> (:x <<removeRule2 (...) (...)>>),
-
- ((literal :x) ::rule) ((literals ::lits))
- <assoc :x :lits `:test equalp> (:x ::r)
- [<linearMatch :rule :r> 't | <removeRule2 :rule :r>:rr] ->
- <consolidateLiterals
- [ <remove (:x ::r) :lits `:test equalp>
- | <substitute (:x ::rr) (:x ::r) :lits `:test equalp> ]>,
-
- :rule ((branches ... :r
- [<linearMatch :rule :r> 't | <removeRule2 :rule :r>:rr] ...)) ->
- <consolidateBranches (... [ | :rr] ...)>;
-
-
- consolidateBranches =
- ~ eliminates singleton branches
-
- (:rule) -> :rule,
-
- :rules -> ((branches ::rules));
-
-
- consolidateLiterals =
- ~ eliminates singleton literals
-
- ((:lit ...)) -> ((literal :lit) ...),
-
- :rules -> ((literals ::rules));
-
-
- ~---------------------------------------------------------------------------------------~
- ~ The rule merger ~
- ~---------------------------------------------------------------------------------------~
- ~
- ~ This merges individual rules into an optimized tree. The tree is optimal both in
- ~ space and execution time.
-
-
- mergeRules (appearance order) =
- ~ merges a set of rules into an optimized tree; the rules are in reverse order
- ~ :rules :tree :appearanceOrder -> :newtree
-
- (:rule ...) :tree :ao <mergeRule :rule :tree :ao :rule> 't :newtree ->
- <mergeRules (...) :newtree :ao>,
-
- ~ if two rules are equally specific, they are kept in appearance order
-
- (:rule ...) :tree 'nil <before :tree :rule> 't ->
- <mergeRules (...) ((branches :tree :rule)) nil>,
-
- (:rule ...) :tree :ao ->
- <mergeRules (...) ((branches :rule :tree)) :ao>,
-
- 'nil :tree :ao ->
- :tree;
-
-
- mergeRule (appearance order) =
- ~ merges a rule into an existing tree of rules
- ~ :rule :tree :appearanceOrder :originalRule -> [t :newtree | nil]
-
- ~ If the left hand sides are identical, replace the right hand side.
-
- ((rewritesTo) ::newrhs) ((rewritesTo) ::oldrhs) :ao :or ->
- <pWarning "existing rule being replaced with:
- " <ruleToString :or>>
- t ((rewritesTo) ::newrhs),
-
- ~ If the first items are identical, merge the remainder of the rules.
-
- (:x ::rule) (:x ::tree) :ao :or
- <mergeRule :rule :tree :ao :or> 't :newtree ->
- t (:x ::newtree),
-
- (:x ::rule) (:x ::tree) :ao :or
- <mergeRule :rule :tree :ao :or> 'nil ->
- t (:x <mergeBranches nil :ao :or :rule (:tree)>),
-
- ~ Merge two or more literals into a literals list.
-
- ((literal :x) ::rule) ((literal :y) ::tree) :ao :or ->
- t ((literals (:x ::rule) (:y ::tree))),
-
- ((literal :x) ::rule) ((literals ::lits)) :ao :or ->
- t (<mergeLiterals :x :rule :lits :ao :or>),
-
- ~ Merge the rule into a branch list.
-
- :rule ((branches ::b)) :ao :or ->
- t (<mergeBranches t :ao :or :rule :b>),
-
- ~ End conditions
-
- 'nil :tree :ao :or ->
- t :tree,
-
- :rule 'nil :ao :or ->
- t :rule,
-
- ~ Otherwise no merging is possible.
-
- :rule :tree :ao :or ->
- nil;
-
-
- mergeLiterals =
- ~ merges a literal into an existing set of literals
- ~ :lit :rule :literals :appearanceOrder :originalRule -> (literals ...)
- ~ :literals = ((atom item item ...) (atom item item ...) ...)
-
- ~ Does the literal exist in the list?
-
- :lit :rule :lits :ao :or
- <assoc :lit :lits `:test equalp> (:lit ::r)
- <mergeRule :rule :r :ao :or> 't :newtree ->
- {do `(rplacd (assoc #$lit #$lits :test #'equalp) #$newtree)}
- (literals ::lits),
-
- :lit :rule :lits :ao :or
- <assoc :lit :lits `:test equalp> (:lit ::r)
- <mergeRule :rule :r :ao :or> 'nil
- <mergeBranches nil :ao :or :rule (:r)> :newbranch ->
- {do `(rplacd (assoc #$lit #$lits :test #'equalp) (list #$newbranch))}
- (literals ::lits),
-
- ~ If not, add it to the beginning of the list (location doesn't really matter).
-
- :lit :rule :lits :ao :or ->
- (literals (:lit ::rule) ::lits);
-
-
- mergeBranches =
- ~ merges a rule into an existing branch of the tree
- ~ :mergeable :appearanceOrder :originalRule :rule :branches -> (branches ...)
- ~ :branches = (rule rule ...)
-
- ~ First try to merge the rule with an existing branch.
-
- 't 'nil :or :rule (... :r <mergeRule :rule :r nil :or> 't :newbranch ...) ->
- (branches ... :newbranch ...),
-
- ~ If we can't change the order of the rules, we can still try to merge the new
- ~ rule with the first branch.
-
- 't 't :or :rule (:r ...) <mergeRule :rule :r t :or> 't :newbranch ->
- (branches :newbranch ...),
-
- ~ Otherwise find a place to insert a new branch.
- ~ Put the rule before the first branch which is not more specific than it.
-
- :me 'nil :or :rule (... :r <before :r :rule> 'nil ...) ->
- (branches ... :rule :r ...),
-
- :me 'nil :or :rule :branches ->
- (branches ::branches :rule),
-
- ~ Keep in appearance order.
-
- :me 't :or :rule :branches ->
- (branches :rule ::branches);
-
-
- before (appearance order) =
- ~ rule1 rule2 -> [t (iff rule1 is more specific than rule2) | nil]
-
- ~ Skip to where something's different.
-
- ((rewritesTo) ...) ((rewritesTo) ...) -> nil, ~ equivalent
- (:x ::rule1) (:x ::rule2) -> <before :rule1 :rule2>,
-
- ~ Longer rules come before shorter rules.
-
- (:x ...) ((rewritesTo) ...) -> t,
- ((rewritesTo) ...) (:x ...) -> nil,
-
- ~ Two literals are equally specific. Otherwise literals come before anything.
-
- ((literal :x) ...) ((literal :y) ...) -> nil,
- ((literal :x) ...) ((literals ...)) -> nil,
- ((literal :x) ...) :rule2 -> t,
-
- ((literals ...)) ((literal :y) ...) -> nil,
- ((literals ...)) ((literals ...)) -> nil,
- ((literals ...)) :rule2 -> t,
-
- :rule1 ((literal :y) ...) -> nil,
- :rule1 ((literals ...)) -> nil,
-
- ~ Treat Lisp values as literals; but put them at the end to encourage factoring.
-
- ((lisp value ...) ...) ((lisp value ...) ...) -> nil,
- ((lisp value ...) ...) ((beginList) ...) -> nil,
- ((lisp value ...) ...) :rule2 -> t,
-
- ~ Lists are equivalent to literals; put them at the end (but in front of
- ~ Lisp values) to encourage factoring.
-
- ((beginList) ...) :rule2 -> t,
- ((endList) ...) :rule2 -> nil,
- ~ rule2 must have a more detailed analysis of the list
-
- :rule1 ((lisp value ...) ...) -> nil,
- :rule1 ((beginList) ...) -> nil,
- :rule1 ((endList) ...) -> t,
- ~ rule1 must have a more detailed analysis of the list
-
- ~ Function calls come before variables.
-
- ((call ...) ...) ((variable ...) ...) -> t,
- ((variable ...) ...) ((call ...) ...) -> nil,
-
- ~ Variables that have already occurred are treated as literals,
- ~ e.g. (:x <some stuff> :x ...) comes before (:x <same stuff> :y ...).
-
- ((variable :x ...) ...) ((variable :y ...) ...) -> {value :x < :y},
- ~ *** full literal treatment needs to be added here!
-
- ~ Single valued variables (:x) come before multiple valued variables (::x).
-
- ((variable :x) ...) ((variable :y 't ..) ...) -> t,
- ((variable :x 't ..) ...) ((variable :y) ...) -> nil,
-
- ~ Check the stopper patterns of multiple variables, e.g. ::x).
-
- ((variable :x 't :pat1) ...) ((variable :y 't :pat2) ...) ->
- <before (::pat1 ...) (::pat2 ...)>,
-
- ~ Repeats are factored based on the specificity of their patterns.
-
- ((repeat ...) ...) :rule2 ->
- <before <strip ((repeat ...) ...)> <strip :rule2>>,
-
- ~ Alternatives are factored based on the specificity of their patterns.
-
- ((alternatives ...) ...) :rule2 ->
- <before <strip ((alternatives ...) ...)> <strip :rule2>>,
-
- ~ Check the other side too.
-
- :rule1 ((repeat ...) ...) ->
- <before :rule1 <strip ((repeat ...) ...)>>,
- :rule1 ((alternatives ...) ...) ->
- <before :rule1 <strip ((alternatives ...) ...)>>,
-
- ~ Otherwise keep rules in their order of appearance.
-
- :rule1 :rule2 -> nil;
-
-
- strip =
- ~ Look at the entire pattern followed by the entire separator pattern.
-
- ((repeat :var :min :pat [:sep]) ...) ->
- <strip (::pat [::sep] ...)>,
-
- ~ Just look at the FIRST alternative. (*** Can this be right??? ***)
-
- ((alternatives :var :alt1 ::others) ...) ->
- <strip (::alt1 ...)>,
-
- :rule -> :rule;
-
-
- ~---------------------------------------------------------------------------------------~
- ~ The rule expander ~
- ~---------------------------------------------------------------------------------------~
- ~
- ~ This expands rules into ordinary Lisp code that can be compiled by the Lisp compiler.
-
-
- expandRules =
- :name :tree ->
- (defpfun :name nil
- (let (!dest !variables !inRepeat)
- <lhs>
- (beginPlispFunction (quote :name))
- <<expandPattern :tree>>
- (endPlispFunction (quote :name)))
- :tree);
-
-
- expandPattern =
- ~ returns a linear list of Common Lisp expressions
-
- :pat -> ( [<<expandItem :pat>>]* );
-
-
- expandItem =
- ~ each Plisp item expands into a list of one or more Common Lisp expressions
-
- (literal :lit) <onLeft> ->
- ((or (nextIs? (quote :lit))
- (failure <humanize (literal :lit)>))),
-
- (literal :lit) <onRight> ->
- ((setq !dest (xCons !dest (quote :lit)))),
-
- (variable :var) <onLeft> ->
- ((slVariable :var)),
-
- (variable :var) <onRight> ->
- ((srVariable :var)),
-
- (variable :var 't 'nil) <onLeft> ->
- ((mlVariable :var t t)),
-
- (variable :var 't ((endList))) <onLeft> ->
- ((mlVariable :var t t)
- <<expandPattern ((endList))>>),
-
- (variable :var 't :pat) <onLeft> ->
- ((cond ((vBound? :var) (mlVariable :var nil t) <<expandPattern :pat>>)
- (t (loop (setDecisionPoint)
- (cond ((empty?)
- (deleteDecisionPoint)
- (failure <humanize (variable :var t :pat)>))
- ((catch !failure <<expandPattern :pat>> nil)
- (restoreDecisionPoint))
- (t (return)))
- (deleteDecisionPoint)
- (mlVariable :var nil nil))))
- (deleteDecisionPoint)),
-
- (variable :var 't :pat) <onRight> ->
- ((mrVariable :var)
- <<expandPattern :pat>>),
-
- (call :fn) [<onLeft> | <onRight>] ->
- (([lCall | rCall] (quote :fn) nil nil)),
-
- (call :fn :pat) [<onLeft> | <onRight>] ->
- (([lCall | rCall] (quote :fn)
- (let ((!dest (xNew)))
- <rhs>
- <<expandPattern :pat>>
- <phs>
- (cdr !dest))
- nil)),
-
- (call :fn 'nil 't) [<onLeft> | <onRight>] ->
- (([lCall | rCall] (quote :fn) nil t)),
-
- (call :fn :pat 't) [<onLeft> | <onRight>] ->
- (([lCall | rCall] (quote :fn)
- (let ((!dest (xNew)))
- <rhs>
- <<expandPattern :pat>>
- <phs>
- (cdr !dest))
- t)),
-
- (beginList) <onLeft> -> ((lBeginList)),
-
- (beginList) <onRight> -> ((rBeginList)),
-
- (endList) <onLeft> -> ((lEndList)),
-
- (endList) <onRight> -> ((rEndList)),
-
- (repeat :var :min :pat [:sep]) ->
- ((let ((!inRepeat t) (!repeatCount 0) (max (repeatMax :var)))
- (loop (setDecisionPoint)
- (cond ~ stop?
- ((or (repeatStop? max)
- [ (and ('> !repeatCount 1)
- (catch !failure <<expandPattern :sep>> nil)
- (restoreDecisionPoint)) ]
- (and (catch !failure <<expandPattern :pat>> nil)
- (restoreDecisionPoint)))
- (deleteDecisionPoint)
- (return)))
- (deleteDecisionPoint))
- (repeatSet :var :min))),
-
- (alternatives :var ::pats) <onLeft> ->
- ((let ((altVar (and (vBound? :var)
- (not !inRepeat) ~ for now (fix it someday!)
- (vEval :var nil))))
- (setDecisionPoint)
- {do :alt := 0}
- (or [ {do :alt := :alt + 1}
- <expandAlternative :pats :var :alt> ]*
- (progn (deleteDecisionPoint)
- (failure <humanize (alternatives :var ::pats)> t)))
- (deleteDecisionPoint))),
-
- (alternatives :var ::pats) <onRight> ->
- ((case (altCheck :var)
- {do :alt := 0}
- [ ({value :alt := :alt + 1} <<expandPattern :pats>>) ]*
- (t (failure <humanize (alternatives :var ::pats)>)))),
-
- (lisp do :e) ->
- (:e),
-
- (lisp if :e [:msg]) ->
- ((or :e (failure [:msg | <humanize (lisp if :e)>]))),
-
- (lisp value :e ['t | 'nil] [:msg]) <onLeft> ->
- ((or ([nextAre? | nextIs?] :e)
- (failure [:msg | <humanize (lisp value :e)>]))),
-
- (lisp value :e ['t | 'nil] [:msg]) <onRight> ->
- ((setq !dest ([xAppend | xCons] !dest :e))),
-
- (rewritesTo) ->
- <rhs> ((setq !dest (xNew))),
-
- (literals ::lits) -> ~ always occurs on left side
- ((case (peek)
- [ <lhs> <expandLiteral :lits> ]*
- (t (failure <humanize (literals ::lits)>)))),
-
- (branches ::pats) -> ~ always occurs on left side
- ((setDecisionPoint)
- (and [ <lhs>
- (catch !failure <<expandPattern :pats>> nil)
- (restoreDecisionPoint) ]*
- (deleteDecisionPoint)
- (failure <humanize (branches ::pats)> t))
- (deleteDecisionPoint)),
-
- :item -> {do pError("unrecognized item in a pattern: ", :item)} (nil);
-
-
- expandAlternative =
- 'nil :var :n ->
- (and (or (null altVar) ('= altVar :n))
- (vSet :var :n nil)),
-
- :pat :var :n ->
- (and (or (null altVar) ('= altVar :n))
- (not (and (catch !failure <<expandPattern :pat>> nil)
- (restoreDecisionPoint)))
- (vSet :var :n nil));
-
-
- expandLiteral =
- (['t | 'nil | otherwise] ...) ->
- (([t | nil | otherwise]) (next) <<expandPattern ..>>),
-
- (:lit ...) -> (:lit (next) <<expandPattern ..>>),
-
- 'nil -> nil;
-